<?php


namespace Soen\Container\Error;


use Psr\EventDispatcher\EventDispatcherInterface;
use Soen\Log\Logger;

class Error
{
    /**
     * @var Logger
     */
    public $logger;
    /**
     * @var EventDispatcherInterface
     */
    public $dispatch;
    
    public function __construct()
    {
        $this->logger = context()->getComponent('log', true);
    }

    public function register(){
        set_error_handler(function ($errno, $errstr, $errfile, $errline){
            $this->exceptionHandle($errno, $errstr, $errfile, $errline);
        });
        register_shutdown_function(function (){
            $errorLast= error_get_last();
            $this->logger->error($errorLast['message'], $errorLast);
        });
    }

    protected function exceptionHandle($errno, $errstr, $errfile, $errline){
//        $this->logger->error($errstr);
        $expection = new \Exception($errstr, $errno);
        $this->log($expection);
    }

    /**
     * 记录异常日志
     * @param \Throwable $ex
     */
    public function log(\Throwable $ex)
    {
        $logger = $this->logger;
        // 构造内容
        list($message, $context) = $this->format($ex, false);
        // 写入
        $level = static::levelType($context['code']);
        switch ($level) {
            case 'error':
                $logger->error($message, $context);
                break;
            case 'warning':
                $logger->warning($message, $context);
                break;
            case 'notice':
                $logger->notice($message, $context);
                break;
        }
    }

    /**
     * 格式化错误数据
     * @param \Throwable $e
     * @param bool $debug
     * @return array
     */
    public function format(\Throwable $e, bool $debug)
    {
        $context = [
            'code'    => $e->getCode(),
//            'message' => $e->getMessage(),
            'file'    => $e->getFile(),
            'line'    => $e->getLine(),
            'type'    => get_class($e),
            'trace'   => explode("\n", $e->getTraceAsString()),
        ];
//        $contextJson = json_encode($context, JSON_UNESCAPED_UNICODE);
        return [$e->getMessage(),$context];
//        foreach ($trace as $key => $value) {
//            if (strpos($value, '): ') !== false) {
//                // 切割为数组
//                $fragments   = [];
//                $tmp         = explode(' ', $value);
//                $fragments[] = array_shift($tmp);
//                $tmp1        = explode('): ', implode(' ', $tmp));
//                $tmp1[0]     .= ')';
//                if (count($tmp1) == 2) {
//                    // IDE 可识别处理，只有放最后才可识别
//                    $fragments[]  = array_pop($tmp1);
//                    $fragments[]  = array_pop($tmp1);
//                    $fragments[2] = str_replace(['.php(', ')'], ['.php on line ', ''], $fragments[2]);
//                    $fragments[2] = 'in ' . $fragments[2];
//                    // 合并
//                    $value = implode(' ', $fragments);
//                }
//            }
//            $trace[$key] = ' ' . $value;
//        }
//        $context['trace'] = implode("\n", $trace);
//        $message          = "{message}\n[code] {code} [type] {type}\n[file] in {file} on line {line}\n{trace}";
//        if (!$debug) {
//            $message = "{message} [{code}] {type} in {file} on line {line}";
//        }
//        return [$message, $context];
    }


    /**
     * 返回错误级别
     * @param $errno
     * @return string
     */
    public static function levelType($errno)
    {
        if (static::isError($errno)) {
            return 'error';
        }
        if (static::isWarning($errno)) {
            return 'warning';
        }
        if (static::isNotice($errno)) {
            return 'notice';
        }
        return 'error';
    }

    /**
     * 是否错误类型
     * 全部类型：http://php.net/manual/zh/errorfunc.constants.php
     * @param $type
     * @return bool
     */
    public static function isError($errno)
    {
        return in_array($errno, [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR]);
    }

    /**
     * 是否警告类型
     * 全部类型：http://php.net/manual/zh/errorfunc.constants.php
     * @param $type
     * @return bool
     */
    public static function isWarning($errno)
    {
        return in_array($errno, [E_WARNING, E_CORE_WARNING, E_COMPILE_WARNING, E_USER_WARNING]);
    }

    /**
     * 是否通知类型
     * 全部类型：http://php.net/manual/zh/errorfunc.constants.php
     * @param $type
     * @return bool
     */
    public static function isNotice($errno)
    {
        return in_array($errno, [E_NOTICE, E_USER_NOTICE, E_DEPRECATED, E_USER_DEPRECATED, E_STRICT]);
    }

    /**
     * 是否为致命错误
     * @param $errno
     * @return bool
     */
    public static function isFatal($errno)
    {
        return in_array($errno, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]);
    }

    /**
     * 是否致命警告类型
     * 特殊的警告，出现后 try/catch 将无法捕获异常。
     * @param $errno
     * @param $errstr
     * @return bool
     */
    public static function isFatalWarning($errno, $errstr)
    {
        if ($errno == E_WARNING && strpos($errstr, 'require') === 0) {
            return true;
        }
        return false;
    }

}